﻿using System;
using System.Collections.ObjectModel;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Web;
using System.Configuration;
using VioletDevelopmentLaboratory.Net.Web.Configuration;
using VioletDevelopmentLaboratory.Net.Data;

namespace VioletDevelopmentLaboratory.Net.Web.Component
{
	public class BoardThreadRatingInfo
	{
		private BoardThreadRatingInfo() { }

		internal BoardThreadRatingInfo(String applicationName, String componentID, String threadID, String providerUserKey, String userName, Int32 point)
		{
			this.applicationName = applicationName;
			this.componentID = componentID;
			this.threadID = threadID;
			this.providerUserKey = providerUserKey;
			this.userName = userName;
			this.point = point;
		}

		private String applicationName;
		public String ApplicationName { get { return applicationName; } }

		private String componentID;
		public String ComponentID { get { return componentID; } }

		private String threadID;
		public String ThreadID { get { return this.threadID; } }

		private String providerUserKey;
		public String ProviderUserKey { get { return this.providerUserKey; } }

		private string userName;
		public String UserName { get { return this.userName; } }

		private Int32 point;
		public Int32 Point { get { return this.point; } }
	}

	public class BoardThreadRatingInfoCollection : Collection<BoardThreadRatingInfo> { }

	public class BoardThread
	{
		private String applicationName;
		/// <summary>
		/// 현재 글을 포함하는 웹에플리케이션의 ApplicationName을 가져옵니다.
		/// </summary>
		public String ApplicationName { get { return applicationName; } internal set { this.applicationName = value; } }

		private String componentID;
		/// <summary>
		/// 현재 글을 포함하는 콤포넌트의 아이디를 가져옵니다.
		/// </summary>
		public String ComponentID { get { return componentID; } internal set { componentID = value; } }

		private String categoryID;
		/// <summary>
		/// 카테고리 아이디를 가져오거나 설정합니다.
		/// </summary>
		public String CategoryID { get { return this.categoryID; } set { this.categoryID = value; } }

		private String threadID;
		/// <summary>
		/// 글의 아이디를 가져옵니다.
		/// </summary>
		public String ThreadID { get { return this.threadID; } internal set { this.threadID = value; } }

		private Int64 threadNumber;
		/// <summary>
		/// 글번호를 가져옵니다.
		/// </summary>
		public Int64 ThreadNumber { get { return this.threadNumber; } internal set { this.threadNumber = value; } }

        private Int64 threadGroup;
		/// <summary>
		/// 그룹번호를 가져옵니다.
		/// </summary>
		public Int64 ThreadGroup { get { return this.threadGroup; } internal set { this.threadGroup = value; } }

		private Int32 threadDepth;
		/// <summary>
		/// 답변깊이단계를 가져옵니다.
		/// </summary>
		public Int32 ThreadDepth { get { return this.threadDepth; } internal set { this.threadDepth = value; } }

        private Int32 threadIndex;
		/// <summary>
		/// 그룹인텍스번호를 가져옵니다.
		/// </summary>
		public Int32 ThreadIndex { get { return this.threadIndex; } internal set { this.threadIndex = value; } }

		private String userName;
		/// <summary>
		/// 작성자 이름을 가져옵니다.
		/// </summary>
		public String UserName { get { return this.userName; } internal set { this.userName = value; } }

		private String providerUserKey;
		/// <summary>
		/// 작성자 공급자키를 가져옵니다.
		/// </summary>
		public String ProviderUserKey { get { return this.providerUserKey; } internal set { this.providerUserKey = value; } }

		private String title;
		/// <summary>
		/// 제목을 가져오거나 설정합니다.
		/// </summary>
		public String Title
		{
			get
			{
				if (this.IsDeleted)
					return VUtility.GetVWebGlobalResource("Board__DeletedThreadTitle");

				if (this.IsBlocked)
					return VUtility.GetVWebGlobalResource("Board__BlockedThreadTitle");

				return HttpContext.Current.Server.HtmlEncode(this.title);
			}
			set { this.title = value; }
		}

		private Boolean isHtml;
		/// <summary>
		/// 내용이 Html형식인지 가져오거나 설정합니다.
		/// </summary>
		public Boolean IsHtml { get { return isHtml; } set { this.isHtml = value; } }

		private String content;
		/// <summary>
		/// 내용을 가져오거나 설정합니다.
		/// </summary>
		public String Content
		{
			get
			{
				if (this.IsBlocked)
					return "";

				return this.content;
			}
			set { this.content = value; }
		}

		private String[] tags;
		/// <summary>
		/// 태그를 가져오거나 설정합니다.
		/// </summary>
		public String[] Tags { get { return this.tags; } set { this.tags = value; } }

		private Int32 readCount;
		/// <summary>
		/// 읽힌 횟수를 가져옵니다.
		/// </summary>
		public Int32 ReadCount { get { return this.readCount; } internal set { this.readCount = value; } }

		private String ip;
		/// <summary>
		/// 작성자 아이피 주소를 가져오거나 설정합니다.
		/// </summary>
		public String IP { get { return String.IsNullOrEmpty(this.ip) ? "0.0.0.0" : this.ip; } set { this.ip = value; } }

		private DateTime registrationDate;
		/// <summary>
		/// 등록 날짜시간을 가져옵니다.
		/// </summary>
		public DateTime RegistrationDate { get { return this.registrationDate; } internal set { this.registrationDate = value; } }

		private String lastUpdateBy;
		/// <summary>
		/// 마지막으로 수정한 사용자의 이름을 가져오거나 설정합니다.
		/// </summary>
		public String LastUpdateBy { get { return this.lastUpdateBy; } set { this.lastUpdateBy = value; } }

		private DateTime lastUpdateDate;
		/// <summary>
		/// 마지막 수정 날짜시간을 가져옵니다.
		/// </summary>
		public DateTime LastUpdateDate { get { return this.lastUpdateDate; } internal set { this.lastUpdateDate = value; } }

        private Int32 ratingCount;
		/// <summary>
		/// 평가 횟수를 가져옵니다.
		/// </summary>
		public Int32 RatingCount { get { return this.ratingCount; } internal set { this.ratingCount = value; } }

        private Int32 ratingPoint;
		/// <summary>
		/// 평가 촘점을 가져옵니다.
		/// </summary>
		public Int32 RatingPoint { get { return this.ratingPoint; } internal set { this.ratingPoint = value; } }

		private String[] links;
		/// <summary>
		/// 관련 링크를 가져오거나 설정합니다.
		/// </summary>
		public String[] Links { get { return this.links; } set { this.links = value; } }

		private Boolean isAnno;
		/// <summary>
		/// 공지사항인지 가져오거나 설정합니다.
		/// </summary>
		public Boolean IsAnno { get { return this.isAnno; } set { this.isAnno = value; } }

        private Boolean isBlocked;
		/// <summary>
		/// 블라인드 상태인지 확인합니다.
		/// </summary>
		public Boolean IsBlocked { get { return this.isBlocked; } internal set { this.isBlocked = value; } }

        private Boolean isClosed;
		/// <summary>
		/// 잠김 상태인지 가져오거나 설정합니다.
		/// </summary>
		public Boolean IsClosed { get { return this.isClosed; } set { this.isClosed = value; } }

        private Boolean isSecret;
		/// <summary>
		/// 비밀글인지 가져오거나 설정합니다.
		/// </summary>
		public Boolean IsSecret { get { return this.isSecret; } set { this.isSecret = value; } }

        private Boolean autoMediaFileView;
		/// <summary>
		/// 첨부된 사진파일 또는 동영상 파일을 본문에 자동으로 출력할지 여부를 가져오거나 설정합니다.
		/// </summary>
		public Boolean AutoMediaFileView { get { return this.autoMediaFileView; } set { this.autoMediaFileView = value; } }

		private Boolean hasVote;
		/// <summary>
		/// 투표를 포함하고 있는지 확인합니다.
		/// </summary>
		public Boolean HasVote { get { return this.hasVote; } internal set { this.hasVote = value; } }

		private Boolean hasFile;
		/// <summary>
		/// 첨부파일이 있는지 확인합니다.
		/// </summary>
		public Boolean HasFile { get { return this.hasFile; } internal set { this.hasFile = value; } }

		/// <summary>
		/// 삭제된 되었는지 확인합니다.
		/// </summary>
		public Boolean IsDeleted
		{
			get
			{
				if (String.IsNullOrEmpty(this.UserName) || String.IsNullOrEmpty(this.ProviderUserKey))
					return true;

				return false;
			}
		}

		private String pageUrl;
		/// <summary>
		/// 링크를 가져옵니다.
		/// </summary>
		public String PageUrl { get { return pageUrl; } internal set { this.pageUrl = value; } }
		
		private String categoryName;
		/// <summary>
		/// 카테고리 이름을 가져옵니다.
		/// </summary>
		public String CategoryName
		{
			get
			{
				if (String.IsNullOrEmpty(this.categoryName))
					return "";

				return this.categoryName;
			}
			set { this.categoryName = value; }
		}

		private Int32 commentCount;
		/// <summary>
		/// 댓글 개수를 가져옵니다.
		/// </summary>
		public Int32 CommentCount { get { return this.commentCount; } internal set { this.commentCount = value; } }

		/// <summary>
		/// 평가 평점을 가져옵니다.
		/// </summary>
		public Decimal Rating
		{
			get
			{
				if (this.RatingCount == 0)
					return 0M;

				return ((Decimal)this.RatingPoint / this.RatingCount);
			}
		}

		private Int32 trackbackCount;
		/// <summary>
		/// 트랙백 개수를 가져옵니다.
		/// </summary>
		public Int32 TrackbackCount { get { return this.trackbackCount; } internal set { this.trackbackCount = value; } }

		public BoardThreadUpdateResult Modify(String[] fileIDs)
		{
			String linksString = "";

			if (this.Links != null)
			{
				foreach (String link in links)
				{
					if (!String.IsNullOrEmpty(link))
						if (System.Text.RegularExpressions.Regex.IsMatch(link, VUtility.GetVWebGlobalResource("General_Expression_Url")))
							linksString += link + "\r\n";
				}
			}

			//기존 태그 삭제
			Tag.RemoveAll(this.ComponentID, this.ThreadID);

			String tagsString = "";

			if (this.Tags != null)
			{
				foreach (String tag in this.Tags)
				{
					if (!String.IsNullOrEmpty(tag.Trim()))
					{
						Tag.Add(this.ComponentID, this.ThreadID, tag.Trim());
						tagsString += tag.Trim() + ",";
					}
				}

				if (tagsString.Length > 0)
					tagsString = tagsString.Substring(0, tagsString.Length - 1);
			}

			Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_Update",
				out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID),
				String.IsNullOrEmpty(this.CategoryID) ? new SqlParameter("@CategoryID", DBNull.Value) : new SqlParameter("@CategoryID", this.CategoryID),
				new SqlParameter("@IsAnno", this.IsAnno),
				new SqlParameter("@IsSecret", this.IsSecret),
				new SqlParameter("@IsClosed", this.IsClosed),
				new SqlParameter("@IP", this.IP),
				String.IsNullOrEmpty(linksString) ? new SqlParameter("@Links", DBNull.Value) : new SqlParameter("@Links", linksString),
				new SqlParameter("@AutoMediaFileView", this.AutoMediaFileView),
				new SqlParameter("@Title", this.Title),
				new SqlParameter("@IsHTML", this.IsHtml),
				String.IsNullOrEmpty(tagsString) ? new SqlParameter("@Tags", DBNull.Value) : new SqlParameter("@Tags", tagsString),
				new SqlParameter("@Content", this.Content),
				new SqlParameter("@UpdateBy", this.LastUpdateBy)
			);

			//파일링크 삭제
			AttachmentFileManager attachmentFileManager = Board.GetBoard(this.ComponentID).GetAttachmentFileManager();
			attachmentFileManager.UnlinkRelationID(this.ThreadID);

			//파일링크 생성
			attachmentFileManager.LinkRelationID(fileIDs, this.ThreadID);

			VUtility.SendSmtpMessage("BoardThread.Modify", this.IP, this.LastUpdateBy, this.Title, this.Content);

			switch ((Int32)returnValue)
			{
				case 0:
					return BoardThreadUpdateResult.Success;
				default:
					return BoardThreadUpdateResult.InvalidThread;
			}
		}

		public BoardThreadDeleteResult Remove()
		{
			Object returnValue = null;

			//첨부파일 삭제
			Board.GetBoard(this.ComponentID).GetAttachmentFileManager().RemoveAll(this.ThreadID);

			//글삭제
			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_Delete",
				out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID)
			);

			switch ((Int32)returnValue)
			{
				case 0:
					return BoardThreadDeleteResult.Success;
				default:
					return BoardThreadDeleteResult.InvalidThread;
			}
		}

		public void ModifyReadCount(Int32 addCount)
		{
			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_UpdateReadCount",
				new SqlParameter("@ThreadID", this.ThreadID),
				new SqlParameter("@AddCount", addCount)
			);
		}

		public BoardThreadSetRatingResult SetRating(String providerUserKey, String userName, Int32 point)
		{
			if (providerUserKey == null)
				throw new ArgumentNullException("providerUserKey");

			if (userName == null)
				throw new ArgumentNullException("userName");

			if (String.IsNullOrEmpty(providerUserKey))
				throw new ArgumentOutOfRangeException("providerUserKey");

			if (String.IsNullOrEmpty(userName))
				throw new ArgumentOutOfRangeException("userName");

			Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThreadRating_Create",
				out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID),
				new SqlParameter("@ProviderUserKey", providerUserKey),
				new SqlParameter("@UserName", userName),
				new SqlParameter("@Point", point)
			);

			switch ((Int32)returnValue)
			{
				case 0:
					return BoardThreadSetRatingResult.Success;
				case 1:
					return BoardThreadSetRatingResult.InvalidThread;
				default:
					return BoardThreadSetRatingResult.Already;
			}
		}

		public BoardThreadRatingInfoCollection GetRatingInfoCollection()
		{
			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThreadRating_GetBoardThreadRating",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID)
			);

			BoardThreadRatingInfoCollection ratingInfoCollection = null;

			try
			{
				if (reader.HasRows)
				{
					ratingInfoCollection = new BoardThreadRatingInfoCollection();

					while (reader.Read())
					{
						ratingInfoCollection.Add(new BoardThreadRatingInfo(
							reader["ApplicationName"].ToString(),
							reader["ComponentID"].ToString(),
							reader["ThreadID"].ToString(),
							reader["ProviderUserKey"].ToString(),
							reader["UserName"].ToString(),
							(Int32)reader["Point"]));
					}
				}
			}
			finally
			{
				reader.Close();
			}

			return ratingInfoCollection;
		}

		public BoardThreadSetBlockResult SetBlock()
		{
			Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_SetBlock",
				out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID)
			);

			switch ((Int32)returnValue)
			{
				case 0:
					return BoardThreadSetBlockResult.Success;
				default:
					return BoardThreadSetBlockResult.InvalidThread;
			}
		}

		public BoardThreadSetClearResult SetClear()
		{
			Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_SetClear",
				out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID)
			);

			switch ((Int32)returnValue)
			{
				case 0:
					return BoardThreadSetClearResult.Success;
				default:
					return BoardThreadSetClearResult.InvalidThread;
			}
		}

		public BoardThreadCollection GetGroupThreadCollection()
		{
			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_GetGroupingBoardThreads",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID)
			);

			BoardThreadCollection threadCollection = null;

			try
			{
				if (reader.HasRows)
				{
					threadCollection = new BoardThreadCollection();

					while (reader.Read())
					{
						threadCollection.Add(BoardThread.Fill(reader));
					}
				}
			}
			finally
			{
				reader.Close();
			}

			return threadCollection;
		}

		public BoardThreadMoveResult Move(String destComponentID, String destCategoryID)
		{
			if (destComponentID == null)
				throw new ArgumentNullException("destComponentID");

			if (String.IsNullOrEmpty(destComponentID))
				throw new ArgumentOutOfRangeException("destComponentID");

			Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_Move",
				out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@SourceComponentID", this.ComponentID),
				new SqlParameter("@SourceThreadID", this.ThreadID),
				new SqlParameter("@DestComponentID", destComponentID),
				String.IsNullOrEmpty(destCategoryID) ? new SqlParameter("@DestCategoryID", DBNull.Value) : new SqlParameter("@DestCategoryID", destCategoryID),
				new SqlParameter("@Content", Content.Replace(".ashx?cid=" + this.ComponentID, ".ashx?cid=" + destComponentID))
			);

			AttachmentFileInfoCollection fileInfoCollection = Board.GetBoard(this.ComponentID).GetAttachmentFileManager().GetAttachmentFileInfoCollection(this.ThreadID);

			if (fileInfoCollection != null)
			{
				foreach (AttachmentFileInfo fileInfo in fileInfoCollection)
				{
					fileInfo.Move(destComponentID);
				}
			}

			switch ((Int32)returnValue)
			{
				case 0: return BoardThreadMoveResult.Success;
				case 1: return BoardThreadMoveResult.InvalidSourceBoardThread;
				case 2: return BoardThreadMoveResult.InvalidDestBoard;
				default: return BoardThreadMoveResult.InvalidDestCategory;
			}
		}






		public BoardCommentCreateResult AddComment(String userName, String providerUserKey, String ip, Boolean isHTML, String content)
		{
			if (userName == null)
				throw new ArgumentNullException("userName");

			if (providerUserKey == null)
				throw new ArgumentNullException("providerUserKey");

			if (ip == null)
				throw new ArgumentNullException("ip");

			if (content == null)
				throw new ArgumentNullException("content");

			if (String.IsNullOrEmpty(userName))
				throw new ArgumentOutOfRangeException("userName");

			if (String.IsNullOrEmpty(providerUserKey))
				throw new ArgumentOutOfRangeException("providerUserKey");

			if (String.IsNullOrEmpty(ip))
				throw new ArgumentOutOfRangeException("ip");

			if (String.IsNullOrEmpty(content))
				throw new ArgumentOutOfRangeException("content");

			Object returnValue = null;

			SqlClientAgent.ExecuteNonQuery(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardComment_Create",
				out returnValue,
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID),
				new SqlParameter("@UserName", userName),
				new SqlParameter("@ProviderUserKey", providerUserKey),
				new SqlParameter("@IP", ip),
				new SqlParameter("@IsHTML", isHTML),
				new SqlParameter("@Content", content)
			);

			VUtility.SendSmtpMessage("BoardComment.Add", ip, userName, "", content);

			switch ((Int32)returnValue)
			{
				case 0:
					return BoardCommentCreateResult.Success;
				default:
					return BoardCommentCreateResult.InvalidThread;
			}
		}

		public BoardComment GetComment(String commentID)
		{
			if (commentID == null)
				throw new ArgumentNullException("commentID");

			if (String.IsNullOrEmpty(commentID))
				throw new ArgumentOutOfRangeException("commentID");

			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardComment_GetBoardComment",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID),
				new SqlParameter("@CommentID", commentID)
			);

			BoardComment comment = null;

			try
			{
				if (reader.HasRows)
				{
					reader.Read();
					comment = BoardComment.Fill(reader);
				}
			}
			finally
			{
				reader.Close();
			}

			return comment;
		}

		public BoardCommentCollection GetCommentCollection()
		{
			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardComment_GetBoardComments",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@ComponentID", this.ComponentID),
				new SqlParameter("@ThreadID", this.ThreadID)
			);

			BoardCommentCollection commentCollection = null;

			try
			{
				if (reader.HasRows)
				{
					commentCollection = new BoardCommentCollection();
					while (reader.Read())
					{
						commentCollection.Add(BoardComment.Fill(reader));
					}
				}
			}
			finally
			{
				reader.Close();
			}

			return commentCollection;
		}








		internal static BoardThread Fill(SqlDataReader reader)
		{
			BoardThread thread = new BoardThread();
			thread.ApplicationName = reader["ApplicationName"].ToString();
			thread.ComponentID = reader["ComponentID"].ToString();
			thread.CategoryID = reader["CategoryID"].ToString();
			thread.CategoryName = reader["CategoryName"].ToString();
			thread.ThreadID = reader["ThreadID"].ToString();
			thread.ThreadNumber = (Int64)reader["ThreadNumber"];
			thread.ThreadGroup = (Int64)reader["ThreadGroup"];
			thread.ThreadDepth = (Int32)reader["ThreadDepth"];
			thread.ThreadIndex = (Int32)reader["ThreadIndex"];
			thread.UserName = reader["UserName"].ToString();
			thread.ProviderUserKey = reader["ProviderUserKey"].ToString();
			thread.IsAnno = (Boolean)reader["IsAnno"];
			thread.IsSecret = (Boolean)reader["IsSecret"];
			thread.IsClosed = (Boolean)reader["IsClosed"];
			thread.IsBlocked = (Boolean)reader["IsBlocked"];
			thread.ReadCount = (Int32)reader["ReadCount"];
			thread.IP = reader["IP"].ToString();
			thread.RegistrationDate = reader["RegistrationDate"] != DBNull.Value ? (DateTime)reader["RegistrationDate"] : DateTime.MinValue;
			thread.AutoMediaFileView = (Boolean)reader["AutoMediaFileView"];
			thread.HasVote = reader["RelationVoteID"] != DBNull.Value ? true : false;
			thread.HasFile = (Int32)reader["FileCount"] > 0 ? true : false;
			thread.LastUpdateBy = reader["LastUpdateBy"].ToString();
			thread.LastUpdateDate = (reader["LastUpdateDate"] != DBNull.Value) ? (DateTime)reader["LastUpdateDate"] : DateTime.MinValue;
			thread.Title = reader["Title"].ToString();
			thread.IsHtml = (Boolean)reader["IsHTML"];
			thread.Content = reader["Content"].ToString();
			thread.CommentCount = (Int32)reader["CommentCount"];
			thread.RatingCount = (Int32)reader["RatingCount"];
			thread.RatingPoint = (Int32)reader["RatingPoint"];

			String dbLinks = reader["Links"].ToString();
			if (!String.IsNullOrEmpty(dbLinks))
				thread.Links = dbLinks.Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

			String dbTags = reader["Tags"].ToString();
			if (!String.IsNullOrEmpty(dbTags))
				thread.Tags = dbTags.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries);

			thread.PageUrl = BoardUtil.GetBoardUrl(reader["PageUrl"].ToString(), BoardPart.View, BoardParameters.PageIndex, BoardParameters.CategoryID, thread.ThreadID, BoardParameters.SearchText, 0, 0);

			return thread;
		}

		internal static BoardThread Fill(DataRow row)
		{
			BoardThread thread = new BoardThread();
			thread.ApplicationName = row["ApplicationName"].ToString();
			thread.ComponentID = row["ComponentID"].ToString();
			thread.CategoryID = row["CategoryID"].ToString();
			thread.CategoryName = row["CategoryName"].ToString();
			thread.ThreadID = row["ThreadID"].ToString();
			thread.ThreadNumber = (Int64)row["ThreadNumber"];
			thread.ThreadGroup = (Int64)row["ThreadGroup"];
			thread.ThreadDepth = (Int32)row["ThreadDepth"];
			thread.ThreadIndex = (Int32)row["ThreadIndex"];
			thread.UserName = row["UserName"].ToString();
			thread.ProviderUserKey = row["ProviderUserKey"].ToString();
			thread.IsAnno = (Boolean)row["IsAnno"];
			thread.IsSecret = (Boolean)row["IsSecret"];
			thread.IsClosed = (Boolean)row["IsClosed"];
			thread.IsBlocked = (Boolean)row["IsBlocked"];
			thread.ReadCount = (Int32)row["ReadCount"];
			thread.IP = row["IP"].ToString();
			thread.RegistrationDate = row["RegistrationDate"] != DBNull.Value ? (DateTime)row["RegistrationDate"] : DateTime.MinValue;
			thread.AutoMediaFileView = (Boolean)row["AutoMediaFileView"];
			thread.HasVote = row["RelationVoteID"] != DBNull.Value ? true : false;
			thread.HasFile = (Int32)row["FileCount"] > 0 ? true : false;
			thread.LastUpdateBy = row["LastUpdateBy"].ToString();
			thread.LastUpdateDate = (row["LastUpdateDate"] != DBNull.Value) ? (DateTime)row["LastUpdateDate"] : DateTime.MinValue;
			thread.Title = row["Title"].ToString();
			thread.IsHtml = (Boolean)row["IsHTML"];
			thread.Content = row["Content"].ToString();
			thread.CommentCount = (Int32)row["CommentCount"];
			thread.RatingCount = (Int32)row["RatingCount"];
			thread.RatingPoint = (Int32)row["RatingPoint"];
			thread.TrackbackCount = (Int32)row["TrackbackCount"];

			String dbLinks = row["Links"].ToString();
			if (!String.IsNullOrEmpty(dbLinks))
				thread.Links = dbLinks.Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

			String dbTags = row["Tags"].ToString();
			if (!String.IsNullOrEmpty(dbTags))
				thread.Tags = dbTags.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries);

			thread.PageUrl = BoardUtil.GetBoardUrl(row["PageUrl"].ToString(), BoardPart.View, BoardParameters.PageIndex, BoardParameters.CategoryID, thread.ThreadID, BoardParameters.SearchText, 0, 0);

			return thread;
		}

		/// <summary>
		/// 검색된 글 가져오기
		/// </summary>
		public static BoardThreadCollection GetSearchingThreadCollection(String searchText, Int32 maximumRows, Int64 startRowIndex)
		{
			if (maximumRows < 1)
				throw new ArgumentOutOfRangeException("maximumRows");

			if (startRowIndex < 0)
				throw new ArgumentOutOfRangeException("startRowIndex");

			SqlDataReader reader = SqlClientAgent.ExecuteReader(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_GetSearchingBoardThreads",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@SearchText", searchText),
				new SqlParameter("@MaximumRows", maximumRows),
				new SqlParameter("@StartRowIndex", startRowIndex)
			);

			BoardThreadCollection threadCollection = null;

			try
			{
				if (reader.HasRows)
				{
					threadCollection = new BoardThreadCollection();
					BoardThread thread = null;

					while (reader.Read())
					{
						thread = new BoardThread();
						thread.ApplicationName = reader["ApplicationName"].ToString();
						thread.ComponentID = reader["ComponentID"].ToString();
						thread.CategoryID = reader["CategoryID"].ToString();
						thread.CategoryName = reader["CategoryName"].ToString();
						thread.ThreadID = reader["ThreadID"].ToString();
						thread.ThreadGroup = (Int64)reader["ThreadGroup"];
						thread.ThreadDepth = (Int32)reader["ThreadDepth"];
						thread.ThreadIndex = (Int32)reader["ThreadIndex"];
						thread.UserName = reader["UserName"].ToString();
						thread.ProviderUserKey = reader["ProviderUserKey"].ToString();
						thread.IsAnno = (Boolean)reader["IsAnno"];
						thread.IsSecret = (Boolean)reader["IsSecret"];
						thread.IsClosed = (Boolean)reader["IsClosed"];
						thread.IsBlocked = (Boolean)reader["IsBlocked"];
						thread.ReadCount = (Int32)reader["ReadCount"];
						thread.IP = reader["IP"].ToString();
						thread.RegistrationDate = (DateTime)reader["RegistrationDate"];
						thread.AutoMediaFileView = (Boolean)reader["AutoMediaFileView"];
						thread.HasVote = reader["RelationVoteID"] != DBNull.Value ? true : false;
						thread.HasFile = (Int32)reader["FileCount"] > 0 ? true : false;
						thread.LastUpdateBy = reader["LastUpdateBy"].ToString();
						thread.LastUpdateDate = (reader["LastUpdateDate"] != DBNull.Value) ? (DateTime)reader["LastUpdateDate"] : new DateTime(0L);
						thread.Title = reader["Title"].ToString();
						thread.IsHtml = (Boolean)reader["IsHTML"];
						thread.Content = reader["Content"].ToString();
						thread.CommentCount = (Int32)reader["CommentCount"];
						thread.RatingCount = (Int32)reader["RatingCount"];
						thread.RatingPoint = (Int32)reader["RatingPoint"];

						String dbLinks = reader["Links"].ToString();
						if (!String.IsNullOrEmpty(dbLinks))
							thread.Links = dbLinks.Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

						String dbTags = reader["Tags"].ToString();
						if (!String.IsNullOrEmpty(dbTags))
							thread.Tags = dbTags.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries);

						thread.PageUrl = BoardUtil.GetBoardUrl(reader["PageUrl"].ToString(), BoardPart.View, BoardParameters.PageIndex, thread.CategoryID, thread.ThreadID, BoardParameters.SearchText, 0, 0);

						threadCollection.Add(thread);
					}
				}
			}
			finally
			{
				reader.Close();
			}

			return threadCollection;
		}

		/// <summary>
		/// 검색된 글 갯수 가져오기
		/// </summary>
		public static Int64 GetSearchingThreadCollectionCount(String searchText)
		{
			Object result = SqlClientAgent.ExecuteScalar(
				ConfigurationManager.ConnectionStrings[VWebConfigurationReader.ConnectionStringName].ConnectionString,
				CommandType.StoredProcedure,
				"VWeb_BoardThread_GetSearchingBoardThreadCount",
				new SqlParameter("@ApplicationName", VWebConfigurationReader.ApplicationName),
				new SqlParameter("@SearchText", searchText)
			);

			return Convert.ToInt64(result);
		}
	}

	public class BoardThreadCollection : Collection<BoardThread> { }	
}
